package leet_code;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * 2、双指针
 * https://leetcode.cn/studyplan/top-100-liked/
 */
public class A_2_DoublePointer {
    /**
     * https://leetcode.cn/problems/move-zeroes/?envType=study-plan-v2&envId=top-100-liked
     * 283. 移动零
     * 给定一个数组 nums，编写一个函数将所有 0 移动到数组的末尾，同时保持非零元素的相对顺序。
     * 请注意 ，必须在不复制数组的情况下原地对数组进行操作。
     * <p>
     * 示例 1:
     * 输入: nums = [0,1,0,3,12]
     * 输出: [1,3,12,0,0]
     * <p>
     * 示例 2:
     * 输入: nums = [0]
     * 输出: [0]
     * <p>
     * <p>
     * 提示:
     * 1 <= nums.length <= 104
     * -231 <= nums[i] <= 231 - 1
     * <p>
     * 进阶：你能尽量减少完成的操作次数吗？
     */
    public void moveZeroes(int[] nums) {
        int firstZero = 0;
        for (int i = 0; i < nums.length; i++) {
            if (nums[i] == 0) {
                if (nums[firstZero] != 0) {
                    firstZero = i;
                    continue;
                }
                firstZero = Math.min(firstZero, i);
                continue;
            }
            if (nums[firstZero] != 0) {
                continue;
            }
            nums[firstZero] = nums[i];
            nums[i] = 0;
            firstZero++;
        }
    }

    /**
     * 11. 盛最多水的容器
     * https://leetcode.cn/problems/container-with-most-water/?envType=study-plan-v2&envId=top-100-liked
     * <p>
     * 给定一个长度为 n 的整数数组 height 。有 n 条垂线，第 i 条线的两个端点是 (i, 0) 和 (i, height[i]) 。
     * 找出其中的两条线，使得它们与 x 轴共同构成的容器可以容纳最多的水。
     * 返回容器可以储存的最大水量。
     * 说明：你不能倾斜容器。
     * 示例 1：
     * 输入：[1,8,6,2,5,4,8,3,7]
     * 输出：49
     * 解释：图中垂直线代表输入数组 [1,8,6,2,5,4,8,3,7]。在此情况下，容器能够容纳水（表示为蓝色部分）的最大值为 49。
     * <p>
     * 示例 2：
     * 输入：height = [1,1]
     * 输出：1
     * <p>
     * <p>
     * 提示：
     * n == height.length
     * 2 <= n <= 10^5
     * 0 <= height[i] <= 10^4
     *
     * @param height
     */
    public int maxArea(int[] height) {
        //方法一：暴力解法
//        int maxArea = 0;
//        int preI = 0;
//        for (int i = 0; i < height.length; i++) {
//            if (height[i] <= preI) {
//                continue;
//            }
//            for (int j = height.length - 1; j > i; j--) {
//                int area = (j - i) * (Math.min(height[i], height[j]));
//                if (area >= maxArea) {
//                    maxArea = area;
//                }
//            }
//            preI = height[i];
//        }
//        return maxArea;
//        -------------------------------------
        //方法二：双指针
        int left = 0;
        int right = height.length - 1;
        int maxArea = 0;
        while (left < right) {
            int area = (right - left) * (Math.min(height[right], height[left]));
            maxArea = Math.max(maxArea, area);
            if (height[left] > height[right]) {
                right--;
                while (left < right && height[right] <= height[right + 1]) {
                    right--;
                }
            }else {
                left++;
                while (left < right && height[left] <= height[left - 1]) {
                    left++;
                }
            }
        }
        return maxArea;
    }

    /**
     * https://leetcode.cn/problems/3sum/?envType=study-plan-v2&envId=top-100-liked
     * 15. 三数之和
     * 给你一个整数数组 nums ，判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i != j、i != k 且 j != k ，同时还满足 nums[i] + nums[j] + nums[k] == 0 。
     * 请你返回所有和为 0 且不重复的三元组。
     * <p>
     * 注意：答案中不可以包含重复的三元组。
     * <p>
     * 示例 1：
     * 输入：nums = [-1,0,1,2,-1,-4]
     * 输出：[[-1,-1,2],[-1,0,1]]
     * 解释：
     * nums[0] + nums[1] + nums[2] = (-1) + 0 + 1 = 0 。
     * nums[1] + nums[2] + nums[4] = 0 + 1 + (-1) = 0 。
     * nums[0] + nums[3] + nums[4] = (-1) + 2 + (-1) = 0 。
     * 不同的三元组是 [-1,0,1] 和 [-1,-1,2] 。
     * 注意，输出的顺序和三元组的顺序并不重要。
     * <p>
     * 示例 2：
     * 输入：nums = [0,1,1]
     * 输出：[]
     * 解释：唯一可能的三元组和不为 0 。
     * 示例 3：
     * <p>
     * 输入：nums = [0,0,0]
     * 输出：[[0,0,0]]
     * 解释：唯一可能的三元组和为 0 。
     * <p>
     * 提示：
     * <p>
     * 3 <= nums.length <= 3000
     * -10^5 <= nums[i] <= 10^5
     *
     * @param nums
     * @return
     */

    public List<List<Integer>> threeSum(int[] nums) {
        Arrays.sort(nums);
        int length = nums.length;
        List<List<Integer>> res = new ArrayList<>();
        for (int i = 0; i < length; i++) {
            if (nums[i] > 0) {
                break;
            }
            if (i > 0 && nums[i] == nums[i - 1]) {
                //元素重复
                continue;
            }
            int L = i + 1;
            int R = length - 1;
            while (L < R) {
                int sum = nums[i] + nums[L] + nums[R];
                if (sum == 0) {
                    res.add(Arrays.asList(nums[i], nums[L], nums[R]));
                    L++;
                    R--;
                    while (L < R && nums[L] == nums[L - 1]) {
                        L++;//去重
                    }
                    while (L < R && nums[R] == nums[R + 1]) {
                        R--;
                    }
                } else if (sum < 0) {
                    L++;
                } else {
                    R--;
                }
            }
        }
        return res;
    }

    /**
     * 42. 接雨水
     * 困难
     * 给定 n 个非负整数表示每个宽度为 1 的柱子的高度图，计算按此排列的柱子，下雨之后能接多少雨水。
     * <p>
     * 示例 1：
     * 输入：height = [0,1,0,2,1,0,1,3,2,1,2,1]
     * 输出：6
     * 解释：上面是由数组 [0,1,0,2,1,0,1,3,2,1,2,1] 表示的高度图，在这种情况下，可以接 6 个单位的雨水（蓝色部分表示雨水）。
     * <p>
     * 示例 2：
     * <p>
     * 输入：height = [4,2,0,3,2,5]
     * 输出：9
     * <p>
     * 提示：
     * n == height.length
     * 1 <= n <= 2 * 10^4
     * 0 <= height[i] <= 10^5
     *
     * @param height
     * @return
     */
    public int trap(int[] height) {
        return 0;
    }


    public static void main(String[] args) {
        A_2_DoublePointer a2DoublePointer = new A_2_DoublePointer();
        //移动零
//        int[] ints = {0,1,0,3,12};
//        int[] ints = {1,0,1};
//        int[] ints = {0};
//        int[] ints = {1,2};
//        a2DoublePointer.moveZeroes(ints);
//        for (int anInt : ints) {
//            System.out.println(anInt);
//        }
//        -------------------------------------
        //最大盛水容器
        int[] ints = {1,8,6,2,5,4,8,3,7};
//        int[] ints = {1,1};
//        int[] ints = {8,8,8,8,8,8,8,8,8};
        System.out.println(a2DoublePointer.maxArea(ints));
//        --------------------------------------
        //三数之和
//        int[] ints = {-1, 0, 1, 2, -1, -4};
//        List<List<Integer>> lists = a2DoublePointer.threeSum(ints);
//        for (List<Integer> list : lists) {
//            for (Integer integer : list) {
//                System.out.print(integer + ", ");
//            }
//            System.out.println();
//        }
    }
}
